package com.stars.easyms.datasource.autoconfigure;

import com.alibaba.druid.support.http.StatViewServlet;
import com.stars.easyms.datasource.batch.BatchCommit;
import com.stars.easyms.datasource.EasyMsMultiDataSource;
import com.stars.easyms.datasource.EasyMsDataSourceFactory;
import com.stars.easyms.datasource.common.EasyMsDataSourceConstant;
import com.stars.easyms.datasource.interceptor.mybatis.EasyMsPageInterceptor;
import com.stars.easyms.datasource.interceptor.mybatis.EasyMsQueryInterceptor;
import com.stars.easyms.datasource.interceptor.mybatis.EasyMsUpdateInterceptor;
import com.stars.easyms.datasource.mybatisplus.EasyMsMybatisPlusConfiguration;
import com.stars.easyms.datasource.pointcut.FixedDataSourceMethodPointcutAdvisor;
import com.stars.easyms.datasource.pointcut.FixedDataSourceTypePointcutAdvisor;
import com.stars.easyms.datasource.pointcut.SpecifyDataSourceMethodPointcutAdvisor;
import com.stars.easyms.datasource.pointcut.SpecifyDataSourceTypePointcutAdvisor;
import com.stars.easyms.datasource.properties.EasyMsPropertiesHelper;
import com.stars.easyms.datasource.mybatis.*;
import com.stars.easyms.datasource.transaction.EasyMsDataSourceTransactionManager;
import com.stars.easyms.datasource.transaction.EasyMsTransactionInterceptor;
import com.stars.easyms.datasource.mybatisplus.EasyMsMybatisPlusHelper;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;

import java.util.*;

/**
 * spring boot数据源及mybatis的配置类，非spring boot项目无需关注该配置类
 *
 * @author guoguifang
 * @date 2018-10-19 10:07
 * @since 1.0.0
 */
@Configuration
public class EasyMsDatasourceAutoConfiguration {

    @Bean
    public ServletRegistrationBean statViewServlet() {
        ServletRegistrationBean<StatViewServlet> statViewServlet = new ServletRegistrationBean<>();
        statViewServlet.setServlet(new StatViewServlet());
        statViewServlet.setName("DruidStatView");
        statViewServlet.addUrlMappings("/druid/*");
        return statViewServlet;
    }

    @Bean
    public EasyMsDataSourceFactory easyMsDataSourceFactory() {
        return new EasyMsDataSourceFactory();
    }

    @Bean
    @Primary
    public EasyMsMultiDataSource easyMsMultiDataSource(EasyMsDataSourceFactory easyMsDataSourceFactory,
                                                       ConfigurableApplicationContext configurableApplicationContext) {
        EasyMsMultiDataSource easyMsMultiDataSource = easyMsDataSourceFactory.createDataSource();
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getAutowireCapableBeanFactory();
        defaultListableBeanFactory.registerSingleton("dataSource", easyMsMultiDataSource);
        return easyMsMultiDataSource;
    }

    @Bean
    @Primary
    public org.apache.ibatis.session.Configuration easyMsMybatisConfiguration() {
        if (EasyMsMybatisPlusHelper.hasMybatisPlus()) {
            return new EasyMsMybatisPlusConfiguration();
        }
        return new EasyMsMybatisConfiguration();
    }

    @Bean
    public EasyMsPageInterceptor easyMsPageInterceptor(EasyMsMultiDataSource dataSource, org.apache.ibatis.session.Configuration easyMsMybatisConfiguration) {
        EasyMsPageInterceptor easyMsPageInterceptor = new EasyMsPageInterceptor(easyMsMybatisConfiguration, dataSource);
        easyMsPageInterceptor.setProperties(EasyMsPropertiesHelper.getEasyMsPageHelperProperties().getProperties());
        return easyMsPageInterceptor;
    }

    @Bean
    @Primary
    public EasyMsSqlSessionFactory easyMsSqlSessionFactory(EasyMsMultiDataSource dataSource, ObjectProvider<Interceptor[]> interceptorsProvider,
                                                           org.apache.ibatis.session.Configuration easyMsMybatisConfiguration) throws Exception {
        EasyMsSqlSessionFactoryBean sqlSessionFactoryBean = new EasyMsSqlSessionFactoryBean(easyMsMybatisConfiguration);
        // 指定数据源
        sqlSessionFactoryBean.setDataSource(dataSource);

        // 指定拦截器及分页插件，有先后顺序这里采用倒序
        Interceptor[] interceptors = interceptorsProvider.getIfAvailable();
        if (interceptors != null) {
            List<Interceptor> interceptorList = Arrays.asList(interceptors);
            AnnotationAwareOrderComparator.sort(interceptorList);
            Collections.reverse(interceptorList);
            sqlSessionFactoryBean.setPlugins(interceptorList.toArray(new Interceptor[0]));
        }

        // 指定基包
        sqlSessionFactoryBean.setTypeAliasesPackage(EasyMsPropertiesHelper.getEasyMsMybatisProperties().getTypeAliasesPackage());
        List<Resource> resourceList = new ArrayList<>();
        for (String mapperLocation : EasyMsPropertiesHelper.getEasyMsMybatisProperties().getMapperLocations()) {
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources(mapperLocation);
            resourceList.addAll(Arrays.asList(resources));
        }
        // 指定xml文件位置
        sqlSessionFactoryBean.setMapperLocations(resourceList.toArray(new Resource[0]));
        return new EasyMsSqlSessionFactory(sqlSessionFactoryBean.getObject());
    }

    @Bean
    @Primary
    public EasyMsSqlSessionTemplate easyMsSqlSessionTemplate(EasyMsSqlSessionFactory easyMsSqlSessionFactory) {
        return new EasyMsSqlSessionTemplate(easyMsSqlSessionFactory);
    }

    @Bean
    @DependsOn({"easyMsMultiDataSource", "easyMsSqlSessionTemplate"})
    public EasyMsMapperScannerConfigurer easyMsMapperScannerConfigurer(ConfigurableApplicationContext applicationContext) {
        EasyMsMapperScannerConfigurer mapperScannerConfigurer = new EasyMsMapperScannerConfigurer(applicationContext);
        mapperScannerConfigurer.setSqlSessionTemplateBeanName("easyMsSqlSessionTemplate");
        mapperScannerConfigurer.setBasePackage(EasyMsPropertiesHelper.getEasyMsMybatisProperties().getTypeAliasesPackage());
        EasyMsDataSourceConstant.SUPPORT_ANNOTATION_TYPE_LIST.forEach(mapperScannerConfigurer::addAnnotationClass);
        if (EasyMsMybatisPlusHelper.hasMybatisPlus()) {
            mapperScannerConfigurer.addMarkerInterface(EasyMsMybatisPlusHelper.getMybatisPlusBaseMapperClass());
        }
        return mapperScannerConfigurer;
    }

    @Bean
    public FixedDataSourceTypePointcutAdvisor fixedDataSourceTypePointcutAdvisor() {
        return new FixedDataSourceTypePointcutAdvisor();
    }

    @Bean
    public FixedDataSourceMethodPointcutAdvisor fixedDataSourceMethodPointcutAdvisor() {
        return new FixedDataSourceMethodPointcutAdvisor();
    }

    @Bean
    public SpecifyDataSourceTypePointcutAdvisor specifyDataSourceTypePointcutAdvisor(EasyMsMultiDataSource dataSource) {
        return new SpecifyDataSourceTypePointcutAdvisor(dataSource);
    }

    @Bean
    public SpecifyDataSourceMethodPointcutAdvisor specifyDataSourceMethodPointcutAdvisor(EasyMsMultiDataSource dataSource) {
        return new SpecifyDataSourceMethodPointcutAdvisor(dataSource);
    }

    @Bean
    public EasyMsQueryInterceptor easyMsQueryInterceptor(EasyMsMultiDataSource dataSource) {
        return new EasyMsQueryInterceptor(dataSource);
    }

    @Bean
    public EasyMsUpdateInterceptor easyMsUpdateInterceptor(EasyMsMultiDataSource dataSource) {
        return new EasyMsUpdateInterceptor(dataSource);
    }

    @Bean
    public BatchCommit batchCommitDAO(EasyMsMultiDataSource dataSource, EasyMsSqlSessionFactory easyMsSqlSessionFactory) {
        return new BatchCommit(dataSource, easyMsSqlSessionFactory);
    }

    @Bean
    public EasyMsDataSourceTransactionManager easyMsDataSourceTransactionManager() {
        return new EasyMsDataSourceTransactionManager();
    }

    @Bean
    public EasyMsTransactionInterceptor easyMsTransactionInterceptor(EasyMsDataSourceTransactionManager easyMsDataSourceTransactionManager,
                                                                     ObjectProvider<BeanFactoryTransactionAttributeSourceAdvisor> advisorObjectProvider) {
        EasyMsTransactionInterceptor easyMsTransactionInterceptor = new EasyMsTransactionInterceptor(easyMsDataSourceTransactionManager);
        BeanFactoryTransactionAttributeSourceAdvisor advisor = advisorObjectProvider.getIfAvailable();
        if (advisor != null) {
            advisor.setAdvice(easyMsTransactionInterceptor);
        }
        return easyMsTransactionInterceptor;
    }
}